using System;
using System.Diagnostics;
using System.Windows.Forms;
using OculusWrap;
using SharpDX;
using SharpDX.D3DCompiler;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using SharpDX.Windows;
using Buffer = SharpDX.Direct3D11.Buffer;
using Device = SharpDX.Direct3D11.Device;
using ovrSession = System.IntPtr;
using ovrTextureSwapChain = System.IntPtr;
using ovrMirrorTexture = System.IntPtr;

namespace OculusWrapDemoDX11
{
    /// <summary>
    /// SharpDX MiniCube Direct3D 11 Sample
    /// </summary>
    static class Program
    {
        private static void Main()
        {
			RenderForm form = new RenderForm("OculusWrap SharpDX demo");

			Wrap					wrap						= new Wrap();
			Hmd						hmd							= null;
			InputLayout				inputLayout					= null;
			Buffer					contantBuffer				= null;
			Buffer					vertexBuffer				= null;
			ShaderSignature			shaderSignature				= null;
			PixelShader				pixelShader					= null;
			ShaderBytecode			pixelShaderByteCode			= null;
			VertexShader			vertexShader				= null;
			ShaderBytecode			vertexShaderByteCode		= null;
			Texture2D				mirrorTextureD3D			= null;
			Layers					layers						= null;
			EyeTexture[]			eyeTextures					= null;
			DeviceContext			immediateContext			= null;
			DepthStencilState		depthStencilState			= null;
			DepthStencilView		depthStencilView			= null;
			Texture2D				depthBuffer					= null;
			RenderTargetView		backBufferRenderTargetView	= null;
			Texture2D				backBuffer					= null;
			SharpDX.DXGI.SwapChain	swapChain					= null;
			Factory					factory						= null;
			MirrorTexture			mirrorTexture				= null;
			Guid					textureInterfaceId			= new Guid("6f15aaf2-d208-4e89-9ab4-489535d34f9c");	// Interface ID of the Direct3D Texture2D interface.

			// Define initialization parameters with debug flag.
			OVRTypes.InitParams initializationParameters = new OVRTypes.InitParams();
			initializationParameters.Flags			= OVRTypes.InitFlags.Debug;

			// Initialize the Oculus runtime.
			bool success = wrap.Initialize(initializationParameters);
			if (!success)
			{
				MessageBox.Show("Failed to initialize the Oculus runtime library.","Uh oh", MessageBoxButtons.OK, MessageBoxIcon.Error);
				return;
			}

			// Use the head mounted display.
			OVRTypes.GraphicsLuid graphicsLuid;
			hmd = wrap.Hmd_Create(out graphicsLuid);
			if (hmd == null)
			{
				MessageBox.Show("Oculus Rift not detected.","Uh oh", MessageBoxButtons.OK, MessageBoxIcon.Error);
				return;
			}

			if (hmd.ProductName == string.Empty) 
			{
				MessageBox.Show("The HMD is not enabled.", "There's a tear in the Rift", MessageBoxButtons.OK, MessageBoxIcon.Error);
				return;
			}

			try
			{
				// Create a set of layers to submit.
				eyeTextures		= new EyeTexture[2];
				OVRTypes.Result	result;

				// Create DirectX drawing device.
				SharpDX.Direct3D11.Device	device	= new Device(SharpDX.Direct3D.DriverType.Hardware, DeviceCreationFlags.Debug);

				// Create DirectX Graphics Interface factory, used to create the swap chain.
				factory = new Factory();

				immediateContext	= device.ImmediateContext;

				// Define the properties of the swap chain.
				SwapChainDescription swapChainDescription						= new SwapChainDescription();
				swapChainDescription.BufferCount								= 1;
				swapChainDescription.IsWindowed									= true;
				swapChainDescription.OutputHandle								= form.Handle;
				swapChainDescription.SampleDescription							= new SampleDescription(1, 0);
				swapChainDescription.Usage										= Usage.RenderTargetOutput | Usage.ShaderInput;
				swapChainDescription.SwapEffect									= SwapEffect.Sequential;
				swapChainDescription.Flags										= SwapChainFlags.AllowModeSwitch;
				swapChainDescription.ModeDescription.Width						= form.Width;
				swapChainDescription.ModeDescription.Height						= form.Height;
				swapChainDescription.ModeDescription.Format						= Format.R8G8B8A8_UNorm;
				swapChainDescription.ModeDescription.RefreshRate.Numerator		= 0;
				swapChainDescription.ModeDescription.RefreshRate.Denominator	= 1;

				// Create the swap chain.
				swapChain	= new SwapChain(factory, device, swapChainDescription);

				// Retrieve the back buffer of the swap chain.
				backBuffer					= swapChain.GetBackBuffer<Texture2D>(0);	
				backBufferRenderTargetView	= new RenderTargetView(device, backBuffer);	

				// Create a depth buffer, using the same width and height as the back buffer.
				Texture2DDescription depthBufferDescription = new Texture2DDescription();
				depthBufferDescription.Format				= Format.D32_Float;
				depthBufferDescription.ArraySize			= 1;
				depthBufferDescription.MipLevels			= 1;
				depthBufferDescription.Width				= form.Width;
				depthBufferDescription.Height				= form.Height;
				depthBufferDescription.SampleDescription	= new SampleDescription(1, 0);
				depthBufferDescription.Usage				= ResourceUsage.Default;
				depthBufferDescription.BindFlags			= BindFlags.DepthStencil;
				depthBufferDescription.CpuAccessFlags		= CpuAccessFlags.None;
				depthBufferDescription.OptionFlags			= ResourceOptionFlags.None;

				// Define how the depth buffer will be used to filter out objects, based on their distance from the viewer.
				DepthStencilStateDescription depthStencilStateDescription	= new DepthStencilStateDescription();
				depthStencilStateDescription.IsDepthEnabled					= true;
				depthStencilStateDescription.DepthComparison				= Comparison.Less;
				depthStencilStateDescription.DepthWriteMask					= DepthWriteMask.Zero;

				// Create the depth buffer.
				depthBuffer			= new Texture2D(device, depthBufferDescription);
				depthStencilView	= new DepthStencilView(device, depthBuffer);	
				depthStencilState	= new DepthStencilState(device, depthStencilStateDescription);
				Viewport			viewport			= new Viewport(0, 0, hmd.Resolution.Width, hmd.Resolution.Height, 0.0f, 1.0f);

				immediateContext.OutputMerger.SetDepthStencilState(depthStencilState);
				immediateContext.OutputMerger.SetRenderTargets(depthStencilView, backBufferRenderTargetView);
				immediateContext.Rasterizer.SetViewport(viewport);

				// Retrieve the DXGI device, in order to set the maximum frame latency.
				using(SharpDX.DXGI.Device1 dxgiDevice = device.QueryInterface<SharpDX.DXGI.Device1>())
				{
					dxgiDevice.MaximumFrameLatency = 1;
				}

				layers		= new Layers();
				LayerEyeFov	layerEyeFov	= layers.AddLayerEyeFov();

				for(int eyeIndex=0; eyeIndex<2; eyeIndex++)
				{
					OVRTypes.EyeType eye				= (OVRTypes.EyeType) eyeIndex;
					EyeTexture	eyeTexture				= new EyeTexture();
					eyeTextures[eyeIndex]				= eyeTexture;

					// Retrieve size and position of the texture for the current eye.
					eyeTexture.FieldOfView				= hmd.DefaultEyeFov[eyeIndex];
					eyeTexture.TextureSize				= hmd.GetFovTextureSize(eye, hmd.DefaultEyeFov[eyeIndex], 1.0f);
					//eyeTexture.RenderDescription		= hmd.GetRenderDesc(eye, hmd.DefaultEyeFov[eyeIndex]);
					eyeTexture.HmdToEyeViewOffset		= eyeTexture.RenderDescription.HmdToEyeOffset;
					eyeTexture.ViewportSize.Position	= new OVRTypes.Vector2i(0, 0);
					eyeTexture.ViewportSize.Size		= eyeTexture.TextureSize;
					eyeTexture.Viewport					= new Viewport(0, 0, eyeTexture.TextureSize.Width, eyeTexture.TextureSize.Height, 0.0f, 1.0f);

					// Define a texture at the size recommended for the eye texture.
					eyeTexture.Texture2DDescription						= new Texture2DDescription();
					eyeTexture.Texture2DDescription.Width				= eyeTexture.TextureSize.Width;
					eyeTexture.Texture2DDescription.Height				= eyeTexture.TextureSize.Height;
					eyeTexture.Texture2DDescription.ArraySize			= 1;
					eyeTexture.Texture2DDescription.MipLevels			= 1;
					eyeTexture.Texture2DDescription.Format				= Format.R8G8B8A8_UNorm;
					eyeTexture.Texture2DDescription.SampleDescription	= new SampleDescription(1, 0);
					eyeTexture.Texture2DDescription.Usage				= ResourceUsage.Default;
					eyeTexture.Texture2DDescription.CpuAccessFlags		= CpuAccessFlags.None;
					eyeTexture.Texture2DDescription.BindFlags			= BindFlags.ShaderResource | BindFlags.RenderTarget;

					// Convert the SharpDX texture description to the Oculus texture swap chain description.
					OVRTypes.TextureSwapChainDesc textureSwapChainDesc = SharpDXHelpers.CreateTextureSwapChainDescription(eyeTexture.Texture2DDescription);

					// Create a texture swap chain, which will contain the textures to render to, for the current eye.
					result = hmd.CreateTextureSwapChainDX(device.NativePointer, textureSwapChainDesc, out eyeTexture.SwapTextureSet);
					WriteErrorDetails(wrap, result, "Failed to create swap chain.");

					// Retrieve the number of buffers of the created swap chain.
					int textureSwapChainBufferCount;
					result = eyeTexture.SwapTextureSet.GetLength(out textureSwapChainBufferCount);
					WriteErrorDetails(wrap, result, "Failed to retrieve the number of buffers of the created swap chain.");

					// Create room for each DirectX texture in the SwapTextureSet.
					eyeTexture.Textures				= new Texture2D			[textureSwapChainBufferCount];
					eyeTexture.RenderTargetViews	= new RenderTargetView	[textureSwapChainBufferCount];

					// Create a texture 2D and a render target view, for each unmanaged texture contained in the SwapTextureSet.
					for (int textureIndex=0; textureIndex<textureSwapChainBufferCount; textureIndex++)
					{
						// Retrieve the Direct3D texture contained in the Oculus TextureSwapChainBuffer.
						IntPtr	swapChainTextureComPtr		= IntPtr.Zero;
						result = eyeTexture.SwapTextureSet.GetBufferDX(textureIndex, textureInterfaceId, out swapChainTextureComPtr);
						WriteErrorDetails(wrap, result, "Failed to retrieve a texture from the created swap chain.");

						// Create a managed Texture2D, based on the unmanaged texture pointer.
						eyeTexture.Textures[textureIndex] = new Texture2D(swapChainTextureComPtr);

						// Create a render target view for the current Texture2D.
						eyeTexture.RenderTargetViews[textureIndex]	= new RenderTargetView(device, eyeTexture.Textures[textureIndex]);
					}

					// Define the depth buffer, at the size recommended for the eye texture.
					eyeTexture.DepthBufferDescription					= new Texture2DDescription();
					eyeTexture.DepthBufferDescription.Format			= Format.D32_Float;
					eyeTexture.DepthBufferDescription.Width				= eyeTexture.TextureSize.Width;
					eyeTexture.DepthBufferDescription.Height			= eyeTexture.TextureSize.Height;
					eyeTexture.DepthBufferDescription.ArraySize			= 1;
					eyeTexture.DepthBufferDescription.MipLevels			= 1;
					eyeTexture.DepthBufferDescription.SampleDescription	= new SampleDescription(1, 0);
					eyeTexture.DepthBufferDescription.Usage				= ResourceUsage.Default;
					eyeTexture.DepthBufferDescription.BindFlags			= BindFlags.DepthStencil;
					eyeTexture.DepthBufferDescription.CpuAccessFlags	= CpuAccessFlags.None;
					eyeTexture.DepthBufferDescription.OptionFlags		= ResourceOptionFlags.None;

					// Create the depth buffer.
					eyeTexture.DepthBuffer		= new Texture2D(device, eyeTexture.DepthBufferDescription);
					eyeTexture.DepthStencilView	= new DepthStencilView(device, eyeTexture.DepthBuffer);

					// Specify the texture to show on the HMD.
					layerEyeFov.ColorTexture[eyeIndex]		= eyeTexture.SwapTextureSet.TextureSwapChainPtr;
					layerEyeFov.Viewport[eyeIndex].Position	= new OVRTypes.Vector2i(0, 0);
					layerEyeFov.Viewport[eyeIndex].Size		= eyeTexture.TextureSize;
					layerEyeFov.Fov[eyeIndex]				= eyeTexture.FieldOfView;
					layerEyeFov.Header.Flags				= OVRTypes.LayerFlags.None;
				}

				OVRTypes.MirrorTextureDesc mirrorTextureDescription	= new OVRTypes.MirrorTextureDesc();
				mirrorTextureDescription.Format					= OVRTypes.TextureFormat.R8G8B8A8_UNORM_SRGB;
				mirrorTextureDescription.Width					= form.Width;
				mirrorTextureDescription.Height					= form.Height;
				mirrorTextureDescription.MiscFlags				= OVRTypes.TextureMiscFlags.None;

				// Create the texture used to display the rendered result on the computer monitor.
				result = hmd.CreateMirrorTextureDX(device.NativePointer, mirrorTextureDescription, out mirrorTexture);
				WriteErrorDetails(wrap, result, "Failed to create mirror texture.");

				// Retrieve the Direct3D texture contained in the Oculus MirrorTexture.
				IntPtr	mirrorTextureComPtr		= IntPtr.Zero;
				result = mirrorTexture.GetBufferDX(textureInterfaceId, out mirrorTextureComPtr);
				WriteErrorDetails(wrap, result, "Failed to retrieve the texture from the created mirror texture buffer.");

				// Create a managed Texture2D, based on the unmanaged texture pointer.
				mirrorTextureD3D = new Texture2D(mirrorTextureComPtr);

				#region Vertex and pixel shader
				// Create vertex shader.
				vertexShaderByteCode	= ShaderBytecode.CompileFromFile("Shaders.fx", "VertexShaderPositionColor", "vs_4_0");
				vertexShader			= new VertexShader(device, vertexShaderByteCode);

				// Create pixel shader.
				pixelShaderByteCode		= ShaderBytecode.CompileFromFile("Shaders.fx", "PixelShaderPositionColor", "ps_4_0");
				pixelShader				= new PixelShader(device, pixelShaderByteCode);
            
				shaderSignature			= ShaderSignature.GetInputSignature(vertexShaderByteCode);

				// Specify that each vertex consists of a single vertex position and color.
				InputElement[] inputElements = new InputElement[]
				{
					new InputElement("POSITION",	0, Format.R32G32B32A32_Float, 0, 0),
					new InputElement("COLOR",		0, Format.R32G32B32A32_Float, 16, 0)
				};

				// Define an input layout to be passed to the vertex shader.
				inputLayout = new InputLayout(device, shaderSignature, inputElements);

				// Create a vertex buffer, containing our 3D model.
				vertexBuffer = Buffer.Create(device, BindFlags.VertexBuffer, m_vertices);

				// Create a constant buffer, to contain our WorldViewProjection matrix, that will be passed to the vertex shader.
				contantBuffer = new Buffer(device, Utilities.SizeOf<Matrix>(), ResourceUsage.Default, BindFlags.ConstantBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0);

				// Setup the immediate context to use the shaders and model we defined.
				immediateContext.InputAssembler.InputLayout			= inputLayout;
				immediateContext.InputAssembler.PrimitiveTopology	= PrimitiveTopology.TriangleList;
				immediateContext.InputAssembler.SetVertexBuffers(0, new VertexBufferBinding(vertexBuffer, sizeof(float)*4*2, 0));
				immediateContext.VertexShader.SetConstantBuffer(0, contantBuffer);
				immediateContext.VertexShader.Set(vertexShader);
				immediateContext.PixelShader.Set(pixelShader);
				#endregion

				DateTime	startTime	= DateTime.Now;
				Vector3		position	= new Vector3(0, 0, -1);

				#region Render loop
				RenderLoop.Run(form, () =>
				{
					OVRTypes.Vector3f[]		hmdToEyeViewOffsets	= {eyeTextures[0].HmdToEyeViewOffset, eyeTextures[1].HmdToEyeViewOffset};
					double					displayMidpoint		= hmd.GetPredictedDisplayTime(0);
					OVRTypes.TrackingState	trackingState		= hmd.GetTrackingState(displayMidpoint, true);
					OVRTypes.Posef[]		eyePoses			= new OVRTypes.Posef[2];
				
					// Calculate the position and orientation of each eye.
					wrap.CalcEyePoses(trackingState.HeadPose.ThePose, hmdToEyeViewOffsets, ref eyePoses);

					float timeSinceStart = (float) (DateTime.Now-startTime).TotalSeconds;

					for(int eyeIndex=0; eyeIndex<2; eyeIndex++)
					{
						OVRTypes.EyeType eye	= (OVRTypes.EyeType) eyeIndex;
						EyeTexture	eyeTexture	= eyeTextures[eyeIndex];

						layerEyeFov.RenderPose[eyeIndex]	= eyePoses[eyeIndex];

						// Update the render description at each frame, as the HmdToEyeOffset can change at runtime.
						eyeTexture.RenderDescription		= hmd.GetRenderDesc(eye, hmd.DefaultEyeFov[eyeIndex]);

						// Retrieve the index of the active texture
						int textureIndex;
				        result = eyeTexture.SwapTextureSet.GetCurrentIndex(out textureIndex);
						WriteErrorDetails(wrap, result, "Failed to retrieve texture swap chain current index.");

						immediateContext.OutputMerger.SetRenderTargets(eyeTexture.DepthStencilView, eyeTexture.RenderTargetViews[textureIndex]);
						immediateContext.ClearRenderTargetView(eyeTexture.RenderTargetViews[textureIndex], Color.Black);
						immediateContext.ClearDepthStencilView(eyeTexture.DepthStencilView, DepthStencilClearFlags.Depth | DepthStencilClearFlags.Stencil, 1.0f, 0);
						immediateContext.Rasterizer.SetViewport(eyeTexture.Viewport);

						// Retrieve the eye rotation quaternion and use it to calculate the LookAt direction and the LookUp direction.
						Quaternion	rotationQuaternion	= SharpDXHelpers.ToQuaternion(eyePoses[eyeIndex].Orientation);
						Matrix		rotationMatrix		= Matrix.RotationQuaternion(rotationQuaternion);
						Vector3		lookUp				= Vector3.Transform(new Vector3(0, -1, 0), rotationMatrix).ToVector3();
						Vector3		lookAt				= Vector3.Transform(new Vector3(0, 0, 1), rotationMatrix).ToVector3();

						Vector3		viewPosition		= position - eyePoses[eyeIndex].Position.ToVector3();

						Matrix world				= Matrix.Scaling(0.1f) * Matrix.RotationX(timeSinceStart/10f) * Matrix.RotationY(timeSinceStart*2/10f) * Matrix.RotationZ(timeSinceStart*3/10f);
						Matrix viewMatrix			= Matrix.LookAtLH(viewPosition, viewPosition+lookAt, lookUp); 

						Matrix projectionMatrix		= wrap.Matrix4f_Projection(eyeTexture.FieldOfView, 0.1f, 100.0f, OVRTypes.ProjectionModifier.LeftHanded).ToMatrix();
						projectionMatrix.Transpose();

						Matrix worldViewProjection	= world * viewMatrix * projectionMatrix;
						worldViewProjection.Transpose();

						// Update the transformation matrix.
						immediateContext.UpdateSubresource(ref worldViewProjection, contantBuffer);

						// Draw the cube
						immediateContext.Draw(m_vertices.Length/2, 0);

						// Commits any pending changes to the TextureSwapChain, and advances its current index
						result = eyeTexture.SwapTextureSet.Commit();
						WriteErrorDetails(wrap, result, "Failed to commit the swap chain texture.");
					}

					result = hmd.SubmitFrame(0, layers);
					WriteErrorDetails(wrap, result, "Failed to submit the frame of the current layers.");

					immediateContext.CopyResource(mirrorTextureD3D, backBuffer);
					swapChain.Present(0, PresentFlags.None);
				});
				#endregion
			}
			finally
			{
				if(immediateContext != null)
				{
					immediateContext.ClearState();
					immediateContext.Flush();
				}

				// Release all resources
				Dispose(inputLayout);
				Dispose(contantBuffer);
				Dispose(vertexBuffer);
				Dispose(shaderSignature);
				Dispose(pixelShader);
				Dispose(pixelShaderByteCode);
				Dispose(vertexShader);
				Dispose(vertexShaderByteCode);
				Dispose(mirrorTextureD3D);
				Dispose(layers);
				Dispose(mirrorTexture);
				Dispose(eyeTextures[0]);
				Dispose(eyeTextures[1]);
				Dispose(immediateContext);
				Dispose(depthStencilState);
				Dispose(depthStencilView);
				Dispose(depthBuffer);
				Dispose(backBufferRenderTargetView);
				Dispose(backBuffer);
				Dispose(swapChain);
				Dispose(factory);

				// Disposing the device, before the hmd, will cause the hmd to fail when disposing.
				// Disposing the device, after the hmd, will cause the dispose of the device to fail.
				// It looks as if the hmd steals ownership of the device and destroys it, when it's shutting down.
				// device.Dispose();
				Dispose(hmd);
				Dispose(wrap);
			}
        }

		/// <summary>
		/// Write out any error details received from the Oculus SDK, into the debug output window.
		/// 
		/// Please note that writing text to the debug output window is a slow operation and will affect performance,
		/// if too many messages are written in a short timespan.
		/// </summary>
		/// <param name="oculus">OculusWrap object for which the error occurred.</param>
		/// <param name="result">Error code to write in the debug text.</param>
		/// <param name="message">Error message to include in the debug text.</param>
		public static void WriteErrorDetails(Wrap oculus, OVRTypes.Result result, string message)
		{
			if(result >= OVRTypes.Result.Success)
				return;

			// Retrieve the error message from the last occurring error.
			OVRTypes.ErrorInfo errorInformation = oculus.GetLastError();

			string formattedMessage = string.Format("{0}. \nMessage: {1} (Error code={2})", message, errorInformation.ErrorString, errorInformation.Result);
			Trace.WriteLine(formattedMessage);
			MessageBox.Show(formattedMessage, message);

			throw new Exception(formattedMessage);
		}

		/// <summary>
		/// Dispose the specified object, unless it's a null object.
		/// </summary>
		/// <param name="disposable">Object to dispose.</param>
		public static void Dispose(IDisposable disposable)
		{
			if(disposable != null)
				disposable.Dispose();
		}

		static Vector4[]	m_vertices = new Vector4[]
		{
			// Near
			new Vector4( 1,  1, -1, 1), new Vector4(1, 0, 0, 1),	
			new Vector4( 1, -1, -1, 1), new Vector4(1, 0, 0, 1),	
			new Vector4(-1, -1, -1, 1), new Vector4(1, 0, 0, 1),	
			new Vector4(-1,  1, -1, 1), new Vector4(1, 0, 0, 1),	
			new Vector4( 1,  1, -1, 1), new Vector4(1, 0, 0, 1),	
			new Vector4(-1, -1, -1, 1), new Vector4(1, 0, 0, 1),	
			
			// Far
			new Vector4(-1, -1,  1, 1), new Vector4(0, 1, 0, 1),	
			new Vector4( 1, -1,  1, 1), new Vector4(0, 1, 0, 1),	
			new Vector4( 1,  1,  1, 1), new Vector4(0, 1, 0, 1),	
			new Vector4( 1,  1,  1, 1), new Vector4(0, 1, 0, 1),	
			new Vector4(-1,  1,  1, 1), new Vector4(0, 1, 0, 1),	
			new Vector4(-1, -1,  1, 1), new Vector4(0, 1, 0, 1),	

			// Left
			new Vector4(-1,  1,  1, 1), new Vector4(0, 0, 1, 1),	
			new Vector4(-1,  1, -1, 1), new Vector4(0, 0, 1, 1),	
			new Vector4(-1, -1, -1, 1), new Vector4(0, 0, 1, 1),	
			new Vector4(-1, -1, -1, 1), new Vector4(0, 0, 1, 1),	
			new Vector4(-1, -1,  1, 1), new Vector4(0, 0, 1, 1),	
			new Vector4(-1,  1,  1, 1), new Vector4(0, 0, 1, 1),	

			// Right
			new Vector4( 1, -1, -1, 1), new Vector4(1, 1, 0, 1),	
			new Vector4( 1,  1, -1, 1), new Vector4(1, 1, 0, 1),	
			new Vector4( 1,  1,  1, 1), new Vector4(1, 1, 0, 1),	
			new Vector4( 1,  1,  1, 1), new Vector4(1, 1, 0, 1),	
			new Vector4( 1, -1,  1, 1), new Vector4(1, 1, 0, 1),	
			new Vector4( 1, -1, -1, 1), new Vector4(1, 1, 0, 1),	

			// Bottom
			new Vector4(-1, -1, -1, 1), new Vector4(1, 0, 1, 1),	
			new Vector4( 1, -1, -1, 1), new Vector4(1, 0, 1, 1),	
			new Vector4( 1, -1,  1, 1), new Vector4(1, 0, 1, 1),	
			new Vector4( 1, -1,  1, 1), new Vector4(1, 0, 1, 1),	
			new Vector4(-1, -1,  1, 1), new Vector4(1, 0, 1, 1),	
			new Vector4(-1, -1, -1, 1), new Vector4(1, 0, 1, 1),	

			// Top
			new Vector4( 1,  1,  1, 1), new Vector4(0, 1, 1, 1),	
			new Vector4( 1,  1, -1, 1), new Vector4(0, 1, 1, 1),	
			new Vector4(-1,  1, -1, 1), new Vector4(0, 1, 1, 1),	
			new Vector4(-1,  1, -1, 1), new Vector4(0, 1, 1, 1),	
			new Vector4(-1,  1,  1, 1), new Vector4(0, 1, 1, 1),
			new Vector4( 1,  1,  1, 1), new Vector4(0, 1, 1, 1)	
		};
	}

	public static class SharpDXHelpers
	{
		/// <summary>
		/// Convert a Vector4 to a Vector3
		/// </summary>
		/// <param name="vector4">Vector4 to convert to a Vector3.</param>
		/// <returns>Vector3 based on the X, Y and Z coordinates of the Vector4.</returns>
		public static Vector3 ToVector3(this Vector4 vector4)
		{
			return new Vector3(vector4.X, vector4.Y, vector4.Z);
		}

		/// <summary>
		/// Convert an ovrVector3f to SharpDX Vector3.
		/// </summary>
		/// <param name="ovrVector3f">ovrVector3f to convert to a SharpDX Vector3.</param>
		/// <returns>SharpDX Vector3, based on the ovrVector3f.</returns>
		public static Vector3 ToVector3(this OVRTypes.Vector3f ovrVector3f)
		{
			return new Vector3(ovrVector3f.X, ovrVector3f.Y, ovrVector3f.Z);
		}

		/// <summary>
		/// Convert an ovrMatrix4f to a SharpDX Matrix.
		/// </summary>
		/// <param name="ovrMatrix4f">ovrMatrix4f to convert to a SharpDX Matrix.</param>
		/// <returns>SharpDX Matrix, based on the ovrMatrix4f.</returns>
		public static Matrix ToMatrix(this OVRTypes.Matrix4f ovrMatrix4f)
		{
			return new Matrix(ovrMatrix4f.M11, ovrMatrix4f.M12, ovrMatrix4f.M13, ovrMatrix4f.M14, ovrMatrix4f.M21, ovrMatrix4f.M22, ovrMatrix4f.M23, ovrMatrix4f.M24, ovrMatrix4f.M31, ovrMatrix4f.M32, ovrMatrix4f.M33, ovrMatrix4f.M34, ovrMatrix4f.M41, ovrMatrix4f.M42, ovrMatrix4f.M43, ovrMatrix4f.M44);
		}

		/// <summary>
		/// Converts an ovrQuatf to a SharpDX Quaternion.
		/// </summary>
		public static Quaternion ToQuaternion(OVRTypes.Quaternionf ovrQuatf)
		{
			return new Quaternion(ovrQuatf.X, ovrQuatf.Y, ovrQuatf.Z, ovrQuatf.W);
		}

		/// <summary>
		/// Creates a TextureSwapChainDesc, based on a specified SharpDX texture description.
		/// </summary>
		/// <param name="texture2DDescription">SharpDX texture description.</param>
		/// <returns>TextureSwapChainDesc, based on the SharpDX texture description.</returns>
		public static OVRTypes.TextureSwapChainDesc CreateTextureSwapChainDescription(Texture2DDescription texture2DDescription)
		{
			OVRTypes.TextureSwapChainDesc textureSwapChainDescription	= new OVRTypes.TextureSwapChainDesc();
			textureSwapChainDescription.Type							= OVRTypes.TextureType.Texture2D;
			textureSwapChainDescription.Format							= GetTextureFormat(texture2DDescription.Format);
			textureSwapChainDescription.ArraySize						= (int)	texture2DDescription.ArraySize;
			textureSwapChainDescription.Width							= (int)	texture2DDescription.Width;
			textureSwapChainDescription.Height							= (int)	texture2DDescription.Height;
			textureSwapChainDescription.MipLevels						= (int)	texture2DDescription.MipLevels;
			textureSwapChainDescription.SampleCount						= (int)	texture2DDescription.SampleDescription.Count;
			textureSwapChainDescription.StaticImage						= 0;
			textureSwapChainDescription.MiscFlags						= GetTextureMiscFlags(texture2DDescription, false);
			textureSwapChainDescription.BindFlags						= GetTextureBindFlags(texture2DDescription.BindFlags);

			return textureSwapChainDescription;
		}

		/// <summary>
		/// Translates a DirectX texture format into an Oculus SDK texture format.
		/// </summary>
		/// <param name="textureFormat">DirectX texture format to translate into an Oculus SDK texture format.</param>
		/// <returns>
		/// Oculus SDK texture format matching the specified textureFormat or OVRTypes.TextureFormat.Unknown if a match count not be found.
		/// </returns>
		public static OVRTypes.TextureFormat GetTextureFormat(SharpDX.DXGI.Format textureFormat)
		{
			switch(textureFormat)
			{
				case SharpDX.DXGI.Format.B5G6R5_UNorm:			return OVRTypes.TextureFormat.B5G6R5_UNORM;
				case SharpDX.DXGI.Format.B5G5R5A1_UNorm:		return OVRTypes.TextureFormat.B5G5R5A1_UNORM;
				case SharpDX.DXGI.Format.R8G8B8A8_UNorm:		return OVRTypes.TextureFormat.R8G8B8A8_UNORM;
				case SharpDX.DXGI.Format.R8G8B8A8_UNorm_SRgb:	return OVRTypes.TextureFormat.R8G8B8A8_UNORM_SRGB;
				case SharpDX.DXGI.Format.B8G8R8A8_UNorm:		return OVRTypes.TextureFormat.B8G8R8A8_UNORM;
				case SharpDX.DXGI.Format.B8G8R8A8_UNorm_SRgb:	return OVRTypes.TextureFormat.B8G8R8A8_UNORM_SRGB;
				case SharpDX.DXGI.Format.B8G8R8X8_UNorm:		return OVRTypes.TextureFormat.B8G8R8X8_UNORM;
				case SharpDX.DXGI.Format.B8G8R8X8_UNorm_SRgb:	return OVRTypes.TextureFormat.B8G8R8X8_UNORM_SRGB;
				case SharpDX.DXGI.Format.R16G16B16A16_Float:	return OVRTypes.TextureFormat.R16G16B16A16_FLOAT;
				case SharpDX.DXGI.Format.D16_UNorm:				return OVRTypes.TextureFormat.D16_UNORM;
				case SharpDX.DXGI.Format.D24_UNorm_S8_UInt:		return OVRTypes.TextureFormat.D24_UNORM_S8_UINT;
				case SharpDX.DXGI.Format.D32_Float:				return OVRTypes.TextureFormat.D32_FLOAT;
				case SharpDX.DXGI.Format.D32_Float_S8X24_UInt:	return OVRTypes.TextureFormat.D32_FLOAT_S8X24_UINT;

				case SharpDX.DXGI.Format.R8G8B8A8_Typeless:		return OVRTypes.TextureFormat.R8G8B8A8_UNORM;
				case SharpDX.DXGI.Format.R16G16B16A16_Typeless:	return OVRTypes.TextureFormat.R16G16B16A16_FLOAT;
	
				default:										return OVRTypes.TextureFormat.UNKNOWN;
			}
		}

		/// <summary>
		/// Creates a set of TextureMiscFlags, based on a specified SharpDX texture description and a mip map generation flag.
		/// </summary>
		/// <param name="texture2DDescription">SharpDX texture description.</param>
		/// <param name="allowGenerateMips">
		/// When set, allows generation of the mip chain on the GPU via the GenerateMips
		/// call. This flag requires that RenderTarget binding also be specified.
		/// </param>
		/// <returns>Created TextureMiscFlags, based on the specified SharpDX texture description and mip map generation flag.</returns>
		public static OVRTypes.TextureMiscFlags GetTextureMiscFlags(Texture2DDescription texture2DDescription, bool allowGenerateMips)
		{
			OVRTypes.TextureMiscFlags results = OVRTypes.TextureMiscFlags.None;

			if(texture2DDescription.Format == SharpDX.DXGI.Format.R8G8B8A8_Typeless || texture2DDescription.Format == SharpDX.DXGI.Format.R16G16B16A16_Typeless)
				results |= OVRTypes.TextureMiscFlags.DX_Typeless;

			if(texture2DDescription.BindFlags.HasFlag(BindFlags.RenderTarget) && allowGenerateMips)
				results |= OVRTypes.TextureMiscFlags.AllowGenerateMips;

			return results;
		}

		/// <summary>
		/// Retrieves a list of flags matching the specified DirectX texture binding flags.
		/// </summary>
		/// <param name="bindFlags">DirectX texture binding flags to translate into Oculus SDK texture binding flags.</param>
		/// <returns>Oculus SDK texture binding flags matching the specified bindFlags.</returns>
		public static OVRTypes.TextureBindFlags GetTextureBindFlags(SharpDX.Direct3D11.BindFlags bindFlags)
		{
			OVRTypes.TextureBindFlags result = OVRTypes.TextureBindFlags.None;

			if(bindFlags.HasFlag(SharpDX.Direct3D11.BindFlags.DepthStencil))
				result |= OVRTypes.TextureBindFlags.DX_DepthStencil;

			if(bindFlags.HasFlag(SharpDX.Direct3D11.BindFlags.RenderTarget))
				result |= OVRTypes.TextureBindFlags.DX_RenderTarget;

			if(bindFlags.HasFlag(SharpDX.Direct3D11.BindFlags.UnorderedAccess))
				result |= OVRTypes.TextureBindFlags.DX_DepthStencil;

			return result;
		}
	}

	/// <summary>
	/// Contains all the fields used by each eye.
	/// </summary>
	public class EyeTexture	:IDisposable
	{
		#region IDisposable Members
		/// <summary>
		/// Dispose contained fields.
		/// </summary>
		public void Dispose()
		{
			if(SwapTextureSet != null)
			{
				SwapTextureSet.Dispose();
				SwapTextureSet = null;
			}

			if(Textures != null)
			{
				foreach(Texture2D texture in Textures)
					texture.Dispose();

				Textures = null;
			}

			if(RenderTargetViews != null)
			{
				foreach(RenderTargetView renderTargetView in RenderTargetViews)
					renderTargetView.Dispose();

				RenderTargetViews = null;
			}

			if(DepthBuffer != null)
			{
				DepthBuffer.Dispose();
				DepthBuffer = null;
			}

			if(DepthStencilView != null)
			{
				DepthStencilView.Dispose();
				DepthStencilView = null;
			}
		}
		#endregion

		public Texture2DDescription				Texture2DDescription;
		public TextureSwapChain					SwapTextureSet;
		public Texture2D[]						Textures;
		public RenderTargetView[]				RenderTargetViews;
		public Texture2DDescription				DepthBufferDescription;
		public Texture2D						DepthBuffer;
		public Viewport							Viewport;
		public DepthStencilView					DepthStencilView;
		public OVRTypes.FovPort					FieldOfView;
		public OVRTypes.Sizei					TextureSize;
		public OVRTypes.Recti					ViewportSize;
		public OVRTypes.EyeRenderDesc			RenderDescription;
		public OVRTypes.Vector3f				HmdToEyeViewOffset;
	}
}
